package org.zjt.config.handler;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.zjt.config.annotation.RemoteValue;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

import static org.zjt.config.common.Constants.FIELD_SEPARATOR;

/**
 * @author <a href="juntao.zhang@bkjk.com">juntao.zhang</a>
 * @Description:
 * @Package com.example.config
 * @date 2017/12/13 15:29
 * @see
 */
@Component
public class BeanFieldValueHandler implements BeanPostProcessor,EnvironmentAware {

    @Autowired
    private DefaultListableBeanFactory defaultListableBeanFactory;

    private Environment environment;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        buildPropertiesBeanFields(bean);
        HandlerFieldsBCCValue(bean);
        HandlerMethodsBCCValue(bean);
        return bean;
    }

    public void HandlerFieldsBCCValue(Object bean) {
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        if (Objects.isNull(declaredFields))
            return ;
        Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(RemoteValue.class))
                .forEach(field -> {
                    RemoteValue annotation = field.getAnnotation(RemoteValue.class);
                    String BCCValue = annotation.value();
                    if (StringUtils.isEmpty(BCCValue)) return;
                    BCCValue= defaultListableBeanFactory.resolveEmbeddedValue(BCCValue);
                    try {
                        field.setAccessible(true);
                        field.set(bean,defaultListableBeanFactory.getTypeConverter().convertIfNecessary(BCCValue,field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                });
    }


    public void HandlerMethodsBCCValue(Object bean) {
        Method[] declaredMethods = bean.getClass().getDeclaredMethods();
        if (Objects.isNull(declaredMethods))
            return ;
        Arrays.stream(declaredMethods).filter(method -> method.isAnnotationPresent(RemoteValue.class))
                .forEach(method -> {
                    RemoteValue annotation = method.getAnnotation(RemoteValue.class);
                    String BCCValue = annotation.value();
                    if (StringUtils.isEmpty(BCCValue)) return;
                    BCCValue = defaultListableBeanFactory.resolveEmbeddedValue(BCCValue);
                    try {
                        method.setAccessible(true);
                        if (method.getParameterCount() != 1 )
                            return;
                        method.invoke(bean,defaultListableBeanFactory.getTypeConverter().convertIfNecessary(BCCValue,method.getParameterTypes()[0]));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
    }



    public void buildPropertiesBeanFields(Object bean) {
        ConfigurationProperties annotation = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);
        if (annotation != null) {
            String prefix = annotation.prefix();
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            if (Objects.nonNull(declaredFields)){
                Arrays.stream(declaredFields).forEach(item ->{
                    String name = prefix + FIELD_SEPARATOR + item.getName();
                    String value = this.environment.getProperty(name);
                    if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(value)){
                        item.setAccessible(true);
                        try {
                            item.set(bean,defaultListableBeanFactory.getTypeConverter().convertIfNecessary(value,item.getType()));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
            }
        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
